package org.bud.rung.netty.util;

import lombok.extern.slf4j.Slf4j;
import org.bud.rung.bit.cron.*;
import org.bud.rung.bit.JobMethod;
import org.bud.rung.constant.Constant;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author shaun
 * @date 2024/8/10
 * @instruction
 */
@Slf4j
public abstract class Crons {
    public static CronExpression crons(String cron){
        CronExpression crons = new CronExpression();
        String[] cronss = cron.split(" ");
        for(int i = 0;i < cronss.length;i++){
            if(cronss[i].equals("*")){
                continue;
            }
            String[] cs = cronss[i].split("/");
            if(i == 0){
                if(cs.length == 1){
                    crons.setSecond(Integer.parseInt(cs[0]));
                }
                if(cs.length > 1){
                    crons.setAdderSecond(Integer.parseInt(cs[1]));
                }
            }
            if(i == 1){
                if(cs.length == 1){
                    crons.setMinute(Integer.parseInt(cs[0]));
                }
                if(cs.length > 1){
                    crons.setAdderMinute(Integer.parseInt(cs[1]));
                }
            }
            if(i == 2){
                if(cs.length == 1){
                    crons.setHour(Integer.parseInt(cs[0]));
                }
                if(cs.length > 1){
                    crons.setAdderHour(Integer.parseInt(cs[1]));
                }
                break;
            }
        }
        return crons;
    }

    public static LocalDateTime shotStamp(CronExpression express,LocalDateTime now,int maxLimitSeconds){
        Integer second = express.getSecond();
        if(second == null){
            second = now.getSecond();
        } else if(second != now.getSecond() && express.getAdderSecond() != null){
            second = (now.getSecond() - second) / express.getAdderSecond();
        }

        Integer minute = express.getMinute();
        if(minute == null){
            minute = now.getMinute();
        } else if(minute != now.getMinute() && express.getAdderMinute() != null){
            minute = (now.getMinute() - minute) / express.getAdderMinute();
        }

        Integer hour = express.getHour();
        if(hour == null){
            hour = now.getHour();
        } else if(hour != now.getHour() && express.getAdderHour() != null){
            hour = (now.getHour() - hour) / express.getAdderHour();
        }

        LocalDateTime pre = LocalDateTime.of(now.toLocalDate(), LocalTime.of(hour,minute,second));
        LocalDateTime last = nextTime(pre,express);

        long preStamp = pre.toEpochSecond(ZoneOffset.ofHours(8));
        long nowStamp = now.toEpochSecond(ZoneOffset.ofHours(8));

        return preStamp + maxLimitSeconds >= nowStamp ? pre : last;
    }

    public static boolean shot(String key,LocalDateTime now,int maxLimitSeconds){
        JobMethod jobMethod = Constant.jobMethodCache.get(key);
        LocalDateTime last;
        CronExpression crons = crons(jobMethod.getJob().cron());
        if(jobMethod.getLast() == null){
            last = shotStamp(crons, now, maxLimitSeconds);
            jobMethod.setLast(last);
        } else {
            last = jobMethod.getLast();
        }

        long lastStamp = last.toEpochSecond(ZoneOffset.ofHours(8));
        long nowStamp = now.toEpochSecond(ZoneOffset.ofHours(8));

        boolean b = lastStamp <= nowStamp && lastStamp + maxLimitSeconds >= nowStamp;
        if(b){
            LocalDateTime post = nextTime(last,crons);
            jobMethod.setLast(post);
        }

        return b;
    }

    private static LocalDateTime nextTime(LocalDateTime last,CronExpression crons){
        int s = last.getSecond() + (crons.getAdderSecond() != null ? crons.getAdderSecond() : 0);
        boolean adders = s >= 60;
        if(adders) s = Optional.ofNullable(crons.getSecond()).orElseGet(() -> LocalDateTime.now().getSecond() + 1);
        adders = s >= 60;
        s %= 60;
        int m = last.getMinute() + (adders ? 1 : (crons.getAdderMinute() != null ? crons.getAdderMinute() : 0)) ;
        boolean adderm = m >= 60;
        if(adderm) m = Optional.ofNullable(crons.getMinute()).orElseGet(() -> LocalDateTime.now().getMinute() + 1);
        adderm = m >= 60;
        m %= 60;
        int h = last.getHour() + (adderm ? 1 : (crons.getAdderHour() != null ? crons.getAdderHour() : 0)) % 60;
        boolean adderh = h >= 24;
        if(adderh) h = Optional.ofNullable(crons.getHour()).orElseGet(() -> LocalDateTime.now().getHour() + 1);
        adderh = h >= 24;
        h %= 24;
        LocalDateTime post = LocalDateTime.of(last.toLocalDate().plusDays(adderh ? 1 : 0 ),LocalTime.of(h,m,s)) ;
        return post;
    }

}
